<template>
  <div style="display: flex; height: 100vh">
    <!-- 控制面板 -->
    <div
      style="
        width: 330px;
        background: #232323;
        color: #fff;
        padding: 20px;
        overflow-y: auto;
        margin-bottom: 30px;
      "
    >
      <h3>三维光照实验室</h3>
      <el-form label-width="85px">
        <template v-for="item in lights" :key="item.name">
          <el-divider>{{ item.label }}</el-divider>
          <el-form-item :label="item.label + '开关'">
            <el-switch v-model="item.on" @change="syncLights" />
          </el-form-item>
          <el-form-item label="强度">
            <el-slider
              v-model="item.intensity"
              :min="0"
              :max="item.max"
              :step="0.01"
              style="width: 180px"
              @input="syncLights"
            />
          </el-form-item>
          <el-form-item label="颜色">
            <el-color-picker
              v-model="item.color"
              @change="syncLights"
              show-alpha
            />
          </el-form-item>
          <el-form-item label="X">
            <el-slider
              v-model="item.x"
              :min="-10"
              :max="10"
              :step="0.1"
              style="width: 180px"
              @input="syncLights"
            />
          </el-form-item>
          <el-form-item label="Y">
            <el-slider
              v-model="item.y"
              :min="-10"
              :max="10"
              :step="0.1"
              style="width: 180px"
              @input="syncLights"
            />
          </el-form-item>
          <el-form-item label="Z">
            <el-slider
              v-model="item.z"
              :min="-10"
              :max="10"
              :step="0.1"
              style="width: 180px"
              @input="syncLights"
            />
          </el-form-item>
        </template>
      </el-form>
    </div>
    <!-- three.js 区域 -->
    <div ref="threeBox" style="flex: 1; background: #222"></div>
  </div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount, watch } from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { RectAreaLightUniformsLib } from "three/examples/jsm/lights/RectAreaLightUniformsLib";

// 灯光参数列表，每个光源一个对象
const lights = ref([
  {
    name: "ambient",
    label: "环境光",
    on: true,
    intensity: 0.4,
    max: 2,
    color: "#ff77ee",
    x: 0,
    y: 2,
    z: 0,
  },
  {
    name: "dir",
    label: "平行光",
    on: true,
    intensity: 1,
    max: 2,
    color: "#ff77ee",
    x: 4,
    y: 8,
    z: 4,
  },
  {
    name: "point",
    label: "点光源",
    on: false,
    intensity: 1.2,
    max: 3,
    color: "#ff77ee",
    x: -2,
    y: 4,
    z: 3,
  },
  {
    name: "spot",
    label: "聚光灯",
    on: false,
    intensity: 2,
    max: 4,
    color: "#00ffcc",
    x: 3,
    y: 7,
    z: 5,
  },
  {
    name: "hemi",
    label: "半球光",
    on: false,
    intensity: 1,
    max: 3,
    color: "#87ceeb",
    x: 0,
    y: 8,
    z: 0,
  },
  {
    name: "rect",
    label: "区域光",
    on: false,
    intensity: 4,
    max: 10,
    color: "#ff77ee",
    x: 0,
    y: 5,
    z: -4,
  },
]);

const threeBox = ref(null);
let renderer, scene, camera, controls, mesh, animateId;
let ambientLight, dirLight, pointLight, spotLight, hemiLight, rectLight;

// helper
let dirLightHelper,
  pointLightHelper,
  spotLightHelper,
  hemiLightHelper,
  rectLightHelper;

onMounted(() => {
  RectAreaLightUniformsLib.init();
  scene = new THREE.Scene();
  scene.background = new THREE.Color(0x232323);

  camera = new THREE.PerspectiveCamera(
    60,
    threeBox.value.clientWidth / threeBox.value.clientHeight,
    0.1,
    100
  );
  camera.position.set(3, 2, 7);

  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(threeBox.value.clientWidth, threeBox.value.clientHeight);
  renderer.shadowMap.enabled = true;
  threeBox.value.appendChild(renderer.domElement);

  controls = new OrbitControls(camera, renderer.domElement);

  // 地面
  const ground = new THREE.Mesh(
    new THREE.PlaneGeometry(12, 12),
    new THREE.MeshStandardMaterial({ color: 0x888888, roughness: 0.7 })
  );
  ground.rotation.x = -Math.PI / 2;
  ground.position.y = -1.2;
  ground.receiveShadow = true;
  scene.add(ground);

  // 球体
  mesh = new THREE.Mesh(
    new THREE.SphereGeometry(1, 48, 48),
    new THREE.MeshStandardMaterial({
      color: 0x3399ff,
      metalness: 0.2,
      roughness: 0.5,
    })
  );
  mesh.position.y = 1;
  mesh.castShadow = true;
  mesh.receiveShadow = true;
  scene.add(mesh);

  scene.add(new THREE.AxesHelper(2));
  addAllLights();
  animate();
});

onBeforeUnmount(() => {
  cancelAnimationFrame(animateId);
  renderer?.dispose?.();
});

// 灯光创建
function addAllLights() {
  // 环境光
  ambientLight = new THREE.AmbientLight(
    lights.value[0].color,
    lights.value[0].intensity
  );
  ambientLight.position.set(
    lights.value[0].x,
    lights.value[0].y,
    lights.value[0].z
  );
  scene.add(ambientLight);

  // 平行光
  dirLight = new THREE.DirectionalLight(
    lights.value[1].color,
    lights.value[1].intensity
  );
  dirLight.position.set(
    lights.value[1].x,
    lights.value[1].y,
    lights.value[1].z
  );
  dirLight.castShadow = true;
  scene.add(dirLight);
  dirLightHelper = new THREE.DirectionalLightHelper(dirLight, 0.4);
  scene.add(dirLightHelper);

  // 点光源
  pointLight = new THREE.PointLight(
    lights.value[2].color,
    lights.value[2].intensity,
    20
  );
  pointLight.position.set(
    lights.value[2].x,
    lights.value[2].y,
    lights.value[2].z
  );
  pointLight.castShadow = true;
  scene.add(pointLight);
  pointLightHelper = new THREE.PointLightHelper(pointLight, 0.2);
  scene.add(pointLightHelper);

  // 聚光灯
  spotLight = new THREE.SpotLight(
    lights.value[3].color,
    lights.value[3].intensity,
    30,
    Math.PI / 7,
    0.2,
    1.5
  );
  spotLight.position.set(
    lights.value[3].x,
    lights.value[3].y,
    lights.value[3].z
  );
  spotLight.castShadow = true;
  scene.add(spotLight);
  spotLightHelper = new THREE.SpotLightHelper(spotLight);
  scene.add(spotLightHelper);

  // 半球光
  hemiLight = new THREE.HemisphereLight(
    lights.value[4].color,
    0x444444,
    lights.value[4].intensity
  );
  hemiLight.position.set(
    lights.value[4].x,
    lights.value[4].y,
    lights.value[4].z
  );
  scene.add(hemiLight);

  // 区域光
  rectLight = new THREE.RectAreaLight(
    lights.value[5].color,
    lights.value[5].intensity,
    4,
    2
  );
  rectLight.position.set(
    lights.value[5].x,
    lights.value[5].y,
    lights.value[5].z
  );
  rectLight.lookAt(0, 0, 0);
  scene.add(rectLight);
}

// 灯光同步方法，所有参数和 helper 都动态响应
function syncLights() {
  console.log(111);

  // 环境光
  ambientLight.intensity = lights.value[0].on ? lights.value[0].intensity : 0;
  ambientLight.color.set(lights.value[0].color);
  ambientLight.position.set(
    lights.value[0].x,
    lights.value[0].y,
    lights.value[0].z
  );

  // 平行光
  dirLight.intensity = lights.value[1].on ? lights.value[1].intensity : 0;
  dirLight.color.set(lights.value[1].color);
  dirLight.position.set(
    lights.value[1].x,
    lights.value[1].y,
    lights.value[1].z
  );
  dirLightHelper.update();

  // 点光源
  pointLight.intensity = lights.value[2].on ? lights.value[2].intensity : 0;
  pointLight.color.set(lights.value[2].color);
  pointLight.position.set(
    lights.value[2].x,
    lights.value[2].y,
    lights.value[2].z
  );
  pointLightHelper.update();

  // 聚光灯
  spotLight.intensity = lights.value[3].on ? lights.value[3].intensity : 0;
  spotLight.color.set(lights.value[3].color);
  spotLight.position.set(
    lights.value[3].x,
    lights.value[3].y,
    lights.value[3].z
  );
  spotLightHelper.update();

  // 半球光
  hemiLight.intensity = lights.value[4].on ? lights.value[4].intensity : 0;
  hemiLight.color.set(lights.value[4].color);
  hemiLight.position.set(
    lights.value[4].x,
    lights.value[4].y,
    lights.value[4].z
  );

  // 区域光
  rectLight.intensity = lights.value[5].on ? lights.value[5].intensity : 0;
  rectLight.color.set(lights.value[5].color);
  rectLight.position.set(
    lights.value[5].x,
    lights.value[5].y,
    lights.value[5].z
  );
  rectLight.lookAt(0, 0, 0);
}

function animate() {
  animateId = requestAnimationFrame(animate);
  mesh.rotation.y += 0.003;
  controls.update();
  renderer.render(scene, camera);
}
</script>
